package modelPackage;

import java.awt.*;
import java.util.ArrayList;
import java.io.*;


/**
 * Loads map and sprite information from a text file.  Stores and provides access to that information.
 * @author Alex
 *
 */
public class Map {
	
	ArrayList<Tile> tiles;
	ArrayList<Sprite> sprites;
	int width;
	int height;
	String building;
	int floor;
	Image background;
	
	ArrayList <Integer> teleportFloors;
	ArrayList <String> teleportBuildings;
	
	
	//Declare some constants.
	static String extension = ".txt";
	static String location = "map/";
	static int TILE_SIZE = 50;
	
	
	static String ilocation = "images/";
	static String tileLocation = "Tiles/";
	static String BGLocation = "Backgrounds/";
	static String iextension = ".jpg";
	
	
	/**
	 * Creates a map from a text file. 
	 * @param filename The file from which to load the map, with no extension or folder location.
	 */
	public Map(String filename) {
		
		//Complete the filename.
		filename = location + filename + extension;
		
		//Create a reader to read text in from files.
		BufferedReader file = null;
		try {
			file = new BufferedReader(new FileReader(filename));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		//Create a list of strings to represent each line of tiles.
		ArrayList <String> lines = new ArrayList<String>();
		String line = null;
		teleportFloors = new ArrayList<Integer>();
		teleportBuildings = new ArrayList<String>();
		
		//Read in each line from the text file.
		while (true) {
			try {
				line = file.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			//If a null line is read in, stop reading lines.
			if (line == null) {
				try {
					file.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			}
			
			boolean add = true;
			//If the line is commented out, ignore it.
			//Comments can be signified by // -- # * ;
			if (line.startsWith("//") || line.startsWith("--") || line.startsWith("#") || line.startsWith("*") || line.startsWith(";")) {
				add = false;
			}
			
			//Then read in the world configuration information, this includes which building and which floor the map is.
			else if (line.startsWith("b:")) {
				building = line.substring(2);
				add = false;
			}
			else if (line.startsWith("f:")) {
				floor = Integer.parseInt(line.substring(2));
				add = false;
			}
			else if (line.startsWith("@:")) {
				int stopParse = 3;
				for (int i = 2; i < line.length(); i++) {
					try {
						Integer.parseInt(line.substring(i,i+1));
					}
					catch (NumberFormatException e) {
						stopParse = i;
						break;
					}
				}
				teleportFloors.add(Integer.parseInt(line.substring(2,stopParse)));
				teleportBuildings.add(line.substring(stopParse));
				add = false;
			}
			else if (line.startsWith("BG:")) {
				background = Toolkit.getDefaultToolkit().getImage(ilocation + BGLocation + line.substring(3) + iextension);
				add = false;
			}
			
			//Otherwise, if the line is valid, put it in the list of lines.
			if (add == true) {
				lines.add(line);
			}
		}
			
		//Now interpret the list of lines and turn them into a list of images.
		//Start by determining width and height.
		//NOTE: width assumes that all lines are the same length.
		height = lines.size();
		width = lines.get(0).length();
		
		//Initialize the lists.
		tiles = new ArrayList<Tile>();
		sprites = new ArrayList<Sprite>();
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		
		int teleportIndex = 0;
		
		if (lines.size() != 0) {
			for (int i = 0; i < height; i++) {
				for (int j = 0; j < width; j++) {
					int xPosition = j*TILE_SIZE;
					int yPosition = i*TILE_SIZE;
					Point position = new Point(xPosition, yPosition);
								
					//If the char is a wall, add a black block.
					if (lines.get(i).charAt(j) == 'A') {
						tiles.add(new Tile(toolkit.getImage(ilocation + tileLocation + "tile.png"), true, position));	
					}
					//If the char is nothing, add a white block.
					//Eventually this will add a transparent block so a background can be used.
					else if (lines.get(i).charAt(j) == ' ') {
						tiles.add(new Tile(toolkit.getImage(ilocation + tileLocation + "clear.png"), false, position));
					}
					//If the char is a sprite, create a sprite and add it to the sprite list.
					else if (lines.get(i).charAt(j) == '1') {
						tiles.add(new Tile(toolkit.getImage(ilocation + tileLocation + "clear.png"), false, position));
						
						Sprite blue = new Bear(position);
						sprites.add(blue);
					}
					else if (lines.get(i).charAt(j) == '@') {
						tiles.add(new TeleportTile(toolkit.getImage(ilocation + tileLocation + "blackhole.png"), false, position, teleportIndex));
						teleportIndex++;
					}
					else if (lines.get(i).charAt(j) == 'B') {
						tiles.add(new Tile(toolkit.getImage(ilocation + tileLocation + "tile.png"), false, position));
					}
				}
			}
		}			
	}
	
	public Image getBackgroundImage() {
		return background;
	}
	
	public ArrayList<Tile> getTiles() {
		return tiles;
	}
	
	public int getHeight() {
		return height;
	}
	
	public int getWidth() {
		return width;
	}	
	
	public ArrayList<Sprite> getSprites() {
		return sprites;
	}
	
	public String getBuilding() {
		return building;
	}
	
	public int getFloor() {
		return floor;
	}
	
	public Tile getSolidTile(Point p) {
		if (tiles.size() != 0) {
			for (int i = 0; i < tiles.size(); i++) {
				if (tiles.get(i).getSolid() == true) {
					if (((p.x >= tiles.get(i).getPosition().x) && (p.x <= tiles.get(i).getPosition().x + TILE_SIZE)) 
						&& ((p.y >= tiles.get(i).getPosition().y) && (p.y <= tiles.get(i).getPosition().y + TILE_SIZE)))
					{
						return tiles.get(i);
					}
				}
			}
		}
		return null;
	}
	
	public Tile getTile(Point p) {
		if (tiles.size() != 0) {
			for (int i = 0; i < tiles.size(); i++) {
				if (((p.x >= tiles.get(i).getPosition().x) && (p.x <= tiles.get(i).getPosition().x + TILE_SIZE)) 
					&& ((p.y >= tiles.get(i).getPosition().y) && (p.y <= tiles.get(i).getPosition().y + TILE_SIZE)))
				{
					return tiles.get(i);
				}
			}
		}
		return null;
	}
	
	public boolean solidTileAnywhereAbove(Tile t) {
		
		int index = t.getPosition().x / TILE_SIZE;
		for (int i = (t.getPosition().y / TILE_SIZE) - 1; i > 0; i--) {
			if (this.tiles.get(i*this.getWidth()  + index).getSolid() == true) {
				return true;
			}
		}
		
		return false;
	}
	
	public boolean solidTileAbove(Tile t) {
		if (tiles.size() != 0) {
			int tLoc = tiles.indexOf(t);
			Point p = indexToCoordinate(tLoc);
			p.y -= 1;
			
			p.x *= TILE_SIZE;
			p.x += TILE_SIZE / 2;
			p.y *= TILE_SIZE;
			p.y += TILE_SIZE / 2;		
			
			if (getSolidTile(p) != null) {
				return true;
			}			
		}
		return false;
	}
	
	public boolean solidTileBelow(Tile t) {
		if (tiles.size() != 0) {
			
			int tLoc = tiles.indexOf(t);
			Point p = indexToCoordinate(tLoc);
			
			p.y += 1;
			
			p.x *= TILE_SIZE;
			p.x += TILE_SIZE / 2;
			p.y *= TILE_SIZE;
			p.y += TILE_SIZE / 2;
			
			
			
			if (getSolidTile(p) != null) {
				return true;
			}			
		}
		return false;
	}
	
	public boolean solidTileRight(Tile t) {
		if (tiles.size() != 0) {
			int tLoc = tiles.indexOf(t);
			Point p = indexToCoordinate(tLoc);
			p.x += 1;
			
			p.x *= TILE_SIZE;
			p.x += TILE_SIZE / 2;
			p.y *= TILE_SIZE;
			p.y += TILE_SIZE / 2;
			
			if (getSolidTile(p) != null) {
				return true;
			}			
		}
		return false;
	}
	
	public boolean solidTileLeft(Tile t) {
		if (tiles.size() != 0) {
			int tLoc = tiles.indexOf(t);
			Point p = indexToCoordinate(tLoc);
			p.x -= 1;
			
			p.x *= TILE_SIZE;
			p.x += TILE_SIZE / 2;
			p.y *= TILE_SIZE;
			p.y += TILE_SIZE / 2;
			
			if (getSolidTile(p) != null) {
				return true;
			}			
		}
		return false;
	}
	
	
	public Point indexToCoordinate(int i) {
		int x = i%width;
		int y = i/width;
		return new Point(x,y);
	}
	
	public int coordinateToIndex(Point p) {
		int index = ((p.y * width) + p.x);
		if (index <= tiles.size()) {
			return index;
		}
		else {
			return -1;
		}
	}
	
	public ArrayList<Integer> getTeleportFloors() {
		return teleportFloors;
	}
	
	public ArrayList<String> getTeleportBuildings() {
		return teleportBuildings;
	}
}